#include "Precompile.h"
#include "Model.h"

namespace Graphics{
	Model::~Model(){
		std::vector<SubModelMeshPtr>::iterator itr = m_subMeshes.begin();
		while(itr != m_subMeshes.end()){
			GraphicsFactory::m_renderContext->m_materialManager->removeMaterial((*itr)->m_materialId);
			itr++;
		}
	}

	void Model::render(RenderContext* context, std::list<unsigned int> &instances){
		std::list<unsigned int> subMeshIds;
		for(unsigned int i=0; i<m_subMeshes.size(); i++){
			subMeshIds.push_back(i);
		}
		m_transparencyCheck = true;
		render(context,instances,subMeshIds);
	}

	void Model::render(RenderContext* context, std::list<unsigned int> &instances, std::list<unsigned int>& subMesh){
		std::list<unsigned int>::iterator	subMeshItr = subMesh.begin(),
											instanceItr = instances.begin();

		m_indBuffer->setBuffer(context);

		std::map<int,std::map<float,std::vector<unsigned int>>> instanceDrawOrder;
		std::vector<unsigned int> fadeingInstance;

		// Sort instances
		std::map<unsigned int, boost::shared_ptr<Mesh>>::iterator mItr;
		std::list<unsigned int>::iterator itr = instances.begin();
		int clip;
		float frame;
		while(itr != instances.end()){
			mItr = m_meshes.find(*itr);
			if(mItr != m_meshes.end()){
				if(mItr->second->isFadeing()){
					fadeingInstance.push_back(*itr);
				}
				else{
					clip = mItr->second->getCurrentAnimationClipId();
					frame = mItr->second->getAnimationMarker();

					instanceDrawOrder[clip][frame].push_back(*itr);
				}
			}
			itr++;
		}

		std::vector<Utility::Math::Matrix<4,4>> jointFrame;
		std::pair<int,float> currentJointFrame;
		currentJointFrame.first = -2;
		currentJointFrame.second = -2;
		bool newFrameComputed = false;
		bool transparentParts = false;

		bool uniqueFrame = false;
		for(; subMeshItr != subMesh.end(); subMeshItr++){
			// Check submesh for transparency and postpone rendering if present
			if(m_transparencyCheck){
				MaterialPtr material = context->m_materialManager->getMaterial(m_subMeshes[*subMeshItr]->m_materialId);
				if(m_id != -1 && material && material->m_colorTransparent.a[0] != 0.0f){
					context->m_renderSystem->m_transparentModels[m_id].first.push_back(*subMeshItr);
					transparentParts = true;
					continue;
				}
			}

			if(m_subMeshes[*subMeshItr]->m_vertBuffer != m_activeVertBuffer){
				m_vertBuffers[m_subMeshes[*subMeshItr]->m_vertBuffer]->setBuffer(context);
				m_activeVertBuffer = m_subMeshes[*subMeshItr]->m_vertBuffer;
			}

			context->m_materialManager->setMaterial(m_subMeshes[*subMeshItr]->m_materialId);

			// Non-fadeing instances first
			std::map<int,std::map<float,std::vector<unsigned int>>>::iterator drawOrderClipItr = instanceDrawOrder.begin();
			std::map<float,std::vector<unsigned int>>::iterator drawOrderFrameItr;
			while(drawOrderClipItr != instanceDrawOrder.end()){
				drawOrderFrameItr = drawOrderClipItr->second.begin();
				while(drawOrderFrameItr != drawOrderClipItr->second.end()){
					if(drawOrderClipItr->first != -1){
						if(currentJointFrame.first != drawOrderClipItr->first || currentJointFrame.first != drawOrderFrameItr->first){
							context->m_animationManager->getAnimationController(m_controllerId)->computeJointFrame(	drawOrderClipItr->first,
																													drawOrderFrameItr->first,
																													jointFrame,
																													context);
							currentJointFrame.first = drawOrderClipItr->first;
							currentJointFrame.second = drawOrderFrameItr->first;
							newFrameComputed = true;
						}
					}
					else{
						if(m_controllerId != -1){
							// Use bindpose matrices
							if(currentJointFrame.first != drawOrderClipItr->first || currentJointFrame.first != drawOrderFrameItr->first){
								jointFrame = context->m_animationManager->getAnimationController(m_controllerId)->getBindPoseMatrices();

								currentJointFrame.first = drawOrderClipItr->first;
								currentJointFrame.second = drawOrderFrameItr->first;
								newFrameComputed = true;
							}
						}
					}

					for(unsigned int j=0; j<drawOrderFrameItr->second.size(); j++){
						mItr = m_meshes.find(drawOrderFrameItr->second[j]);
						if(mItr != m_meshes.end()){
							if(newFrameComputed){
								context->m_materialManager->setJointFrame(jointFrame,m_controllerId);
								newFrameComputed = false;
							}

							context->m_materialManager->setInstanceShaderVariables(mItr->second);
							mItr->second->render(context,m_subMeshes[*subMeshItr]->m_nuInds,m_subMeshes[*subMeshItr]->m_indOffset);
						}
					}
					drawOrderFrameItr++;
				}
				
				drawOrderClipItr++;
			}

			// Fadeing instances
			for(unsigned int j=0; j<fadeingInstance.size(); j++){
				mItr = m_meshes.find(fadeingInstance[j]);
				if(mItr != m_meshes.end()){
					unsigned int sourceClip = mItr->second->getLastAnimationClipId();
					unsigned int targetClip = mItr->second->getCurrentAnimationClipId();
					float sourceFrame = mItr->second->getLastAnimationMarker();
					float targetFrame = mItr->second->getAnimationMarker();
					float interpolate = (mItr->second->getFadeTime()-mItr->second->getCurrentFadeTime())/mItr->second->getFadeTime();

					std::vector<Utility::Math::Matrix<4,4>> jointFrameFade;
					context->m_animationManager->getAnimationController(m_controllerId)->computeJointFrame(sourceClip,targetClip,sourceFrame,targetFrame,interpolate,jointFrameFade,context);
					
					context->m_materialManager->setJointFrame(jointFrameFade,m_controllerId);
					context->m_materialManager->setInstanceShaderVariables(mItr->second);
					mItr->second->render(context,m_subMeshes[*subMeshItr]->m_nuInds,m_subMeshes[*subMeshItr]->m_indOffset);
				}
			}
		}

		// Copy instance list to transparency rendering structure
		if(m_id != -1 && transparentParts){
			context->m_renderSystem->m_transparentModels[m_id].second = instances;
		}

		m_transparencyCheck = false;
		m_activeVertBuffer = -1;
	}

	void Model::addInstance(boost::shared_ptr<Mesh> mesh){
		m_meshes[mesh->m_id] = mesh;
	}

	void Model::removeInstance(boost::shared_ptr<Mesh> mesh){
		std::map<unsigned int, MeshPtr>::iterator itr = m_meshes.find(mesh->m_id);
		if(itr == m_meshes.end()){
			LOG_ERROR("Mesh not present in model instance list.");
			assert(0);
			return;
		}

		m_meshes.erase(itr);
	}

	unsigned int Model::addVertexBuffer(VertexBufferPtr vertBuffer){
		unsigned int id = m_vertBufferId++;
		m_vertBuffers[id] = vertBuffer;
		return id;
	}

	VertexBufferPtr Model::getVertexBuffer(unsigned int id){
		std::map<unsigned int,VertexBufferPtr>::iterator itr = m_vertBuffers.find(id);
		if(itr == m_vertBuffers.end()){
			LOG_ERROR("No vertex buffer present with that id.");
			assert(0);
			return VertexBufferPtr();
		}
		return itr->second;
	}
}